﻿String.prototype.trim = function () { return this.replace(/(^\s*)|(\s*$)/g, ""); }
// 如果转换失败，将返回 null
// defaultValue 可以不填，默认为null
//TODO 当前还不能转换为时间
String.prototype.toDate = function (defaultDate) {
    if (defaultDate != null && !(defaultDate instanceof Date)) {
        defaultDate = defaultDate.toString().toDate();
    }

    if (defaultDate == undefined)
        defaultDate = null;

    var date = null;
    var arr = new Array();
    if (this.indexOf("-") != -1) {
        arr = this.toString().split("-");
    } else if (this.indexOf("/") != -1) {
        arr = this.toString().split("/");
    } else {
        return defaultDate;
    }

    //yyyy-mm-dd || yyyy/mm/dd
    if (arr[0].length == 4) {
        date = new Date(arr[0], arr[1] - 1, arr[2]);
        if (date.getFullYear() == arr[0] && date.getMonth() == arr[1] - 1 && date.getDate() == arr[2]) {
            return date;
        }
    }
    //dd-mm-yyyy || dd/mm/yyyy
    if (arr[2].length == 4) {
        date = new Date(arr[2], arr[1] - 1, arr[0]);
        if (date.getFullYear() == arr[2] && date.getMonth() == arr[1] - 1 && date.getDate() == arr[0]) {
            return date;
        }
    }
    //mm-dd-yyyy || mm/dd/yyyy
    if (arr[2].length == 4) {
        date = new Date(arr[2], arr[0] - 1, arr[1]);
        if (date.getFullYear() == arr[2] && date.getMonth() == arr[0] - 1 && date.getDate() == arr[1]) {
            return date;
        }
    }

    return defaultDate;
}

String.prototype.isDate = function () {
    return this.toDate() != null;
}


/* 
返回新值，並且不改變舊值
datePart yy:year, m:month, d: date,h:hour, mi:minute, s:second, ms:毫秒
*/
Date.prototype.add = function (datePart, v) {
    var reg = /^((yy)|(m)|(d)|(h)|(mi)|(s)|(ms))$/i;
    if (!reg.test(datePart)) {
        throw new Error("Invalid argument datePart, yy:year, m:month, d: date,h:hour, mi:minute, s:second, ms:millisecond");
    }

    if (isNaN(v)) {
        return this;
    }

    // Date 是地址引用
    var tmp = new Date(this.valueOf());

    var key = datePart.match(reg)[0].toUpperCase();
    var vv = this.valueOf();
    switch (key) {
        case "YY":
            vv = tmp.setFullYear(this.getFullYear() + v);
            break;
        case "M":
            vv = tmp.setMonth(this.getMonth() + v);
            break;
        case "D":
            vv = tmp.setDate(this.getDate() + v);
            break;
        case "H":
            vv = tmp.setHours(this.getHours() + v);
            break;
        case "MI":
            vv = tmp.setMinutes(this.getMinutes() + v);
            break;
        case "S":
            vv = tmp.setSeconds(this.getSeconds() + v);
            break;
        case "MS":
            vv = tmp.setMilliseconds(this.getMilliseconds() + v);
            break;
    }
    return new Date(vv);
}



var FormValidateForEntLib = {};
(function ($) {
    
    var validator = function( ){
        this.errorList = []; 

        /*
        onPass, onUnpass 为回调函数，可不写这两个参数，如果写了这两个参数，它们必须为 function, 否则不执行
        */
        this.validate = function(form, rules, mode, onPass, onUnpass){
            return validate(this, form, rules, mode, onPass, onUnpass);
        }

        this.addError = function (rule) {
            this.errorList.push(rule);
        }

        this.errToString = function () {
            var t = "", rule;
            for (var i = 0; rule = this.errorList[i]; i++)
                t += (i + 1) + ". " + rule.msg + "\n";

            return t;
        }
    }

    //入口函数
    $.getInstance = function(){
        return new validator();
    }

    //EntLib
    var CONTAINS="$.contains(obj , rule)";
    var LENGTH="$.strLen(obj , rule)";
    var NOTNULL= "rule.min = 1;$.strLen(obj, rule)";
    var RANGE="$.checkNumber('Number', obj , rule)";//数字范围
    var DATERANGE="$.dateRange(obj , rule)";
    var RELATIVEDATETIME="$.relativeDateRange(obj , rule)";
    //var DOMAIN="$.testDomain(obj , rule)";//DOMAIN 以正则表达式形式表现
    var COMPARE= "$.compare(form , obj , rule)";
    var AND="$.and(form , rule)";
    var OR="$.or(form , rule)";
    var REGEX = "$.testRegex(obj,rule)";
    

    var REQUIRE = /.+/;
    var EMAIL = /^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/;
    var URL = /^http(s)?:\/\/([\w-]+\.)+[\w-]+(\/[\w- .\/?%&=]*)?$/i;
    var NUM = "$.checkNumber('Number',obj,rule)";
    var INT = "$.checkNumber('Int',obj,rule)";
    var DOUBLE = "$.checkNumber('Double',obj,rule)";
    var ENGLISH = /^[A-Za-z]+$/;
    var CHINESE = /^[\u0391-\uFFE5]+$/;
    var FILTER = "$.doFilter(obj,rule)";
    var DATE = "$.isDate(obj, rule)";
    var TIME = "$.isTime(obj,rule)";
    var REPEAT = "rule.operator = '==' ; $.compare( form , obj ,rule )";
    var GROUP = "$.mustChecked(obj,rule)";
    var FUN = "$.testFun(obj, rule)";

    $.extend = function (form, tag, rule) {
        //先取出以存在的规则，以免重复
        var names = {}, tmp;
        for (var i = 0; tmp = rule[i]; i++) {
            names[tmp.name] = true;
        }

        var item, orgRule, newRule, o;
        for (var i = 0; item = form[i]; i++) {
            //如果 name 不存在于 以存在的规则里，并且设置了 tag 所指的属性
            if (!(item.name in names) && item.getAttribute(tag) != "" && item.getAttribute(tag) != null) {
                if (rule[item.getAttribute(tag)] == undefined) continue; //如果要 copy 的规则不存在，

                orgRule = rule[item.getAttribute(tag)];
                newRule = {}; //newRule = {} 不可放在 for 外面定义，否则在IE下还是地址引用
                // copy rule,因为javascript 把对象赋值给一个变量 是 地址引用，所以，只有能用下面这个办法。
                for (o in orgRule) {
                    newRule[o] = orgRule[o];
                }
                newRule.name = item.name;
                rule.push(newRule);
                names[item.name] = true;
            }
        }
    }

    var span_ = document.createElement("SPAN");
    span_.className = "tip";

    $.onPass = function (obj, rule) {
        obj_ = obj.parentNode || obj[obj.length - 1].parentNode;
        try {
            obj_.removeChild(obj_.errorTip);
            obj_.errorTip = null
        } catch (e) { }
    }

    $.onUnpass = function (obj, rule) {
        obj_ = obj.parentNode || obj[obj.length - 1].parentNode;
        if (!obj_.errorTip) {
            obj_.errorTip = span_.cloneNode(true);
            obj_.appendChild(obj_.errorTip);
        }
        obj_.errorTip.innerHTML = "<span class='warnning'></span>" + rule.msg;
    }

    //var errList = [];

    var addEvent = function (obj, type, fun) {
        if (obj.attachEvent) {
            if (!/^on/.test(type))
                type = 'on' + type;
            obj.attachEvent(type, fun);
        } else if (obj.addEventListener) {
            if (/^on/.test(type))
                type = type.substr(2);
            obj.addEventListener(type, fun, false);
        }
    }

    var doFunction = function (fun) {
        var args = [], i;
        for (i = 1; i < arguments.length; i++) {
            args.push(arguments[i]);
        }
        return function () {
            fun.apply(null, args);
            delete args;
        };
    }

    $.validateOnEvent = function (form, rules, evt, onPass, onUnpass) {
        var i, rule, obj;
        for (i = 0; rule = rules[i]; i++) {
            obj = form[rule.name];
            if (!obj) continue;
            addEvent(obj, evt, doFunction(checkByEvent, form, rule, onPass, onUnpass));
        }
    }

    var checkByEvent = function (form, rule, onPass, onUnpass) {
        var f = $.check(form, rule);
        var funU = onUnpass || $.onUnpass;
        var funP = onPass || $.onPass;
        if (f === false && typeof (funU) == "function") {
            funU(form[rule.name], rule);
            return false;
        } else if (f === true && typeof (funP) == "function") {
            funP(form[rule.name], rule)
        }
    }

    /*
    onPass, onUnpass 为回调函数，可不写这两个参数，如果写了这两个参数，它们必须为 function, 否则不执行
    */
    var validate = function (validator , form, rules, mode, onPass, onUnpass) {
        //清理上一次的错误
        validator.errorList.length = 0;
        mode = mode || 1;
        /*----------------------------
        1,全部检查,并alert
        2,一个一个检查，并alert,focus
        3,调用自定义事件 onPassFun验证通过时调用，onUnpassFun未通过时调用。
        4,同3,但是当验证不通过时，立即停止向下检查。
        ----------------------------*/

        var ms = new Date().valueOf();

        var f, rule;
        for (var i = 0; i < rules.length; i++) {
            rule = rules[i];
            if (rule == undefined) continue;

            //设置规则的id
            //同一控件,可以应用多个规则.如果前面的规则失败,后面的规则成后,在自定义的 onPass 和 onUnpass 里不好处理.
            //加这个 id 就是为了解决这个问题
            if(!rule.id)
                rule.id = (ms + i).toString(32);

            f = check(form, rule);

            if (mode == 2 && f === false) {
                alert(rule.msg);
                try {
                    form[rule.name].select();
                } catch (e) {

                }

                try {
                    form[rule.name].focus();
                } catch (e) {
                    form[rule.name][0].focus();
                }
                return false;
            } else if (mode == 3 && f === true) {
                var fun = onPass || $.onPass;
                typeof (fun) == "function" ? fun(form[rule.name], rule) : null;
            } else if (mode == 3 && f === false) {
                //$.addError(rule);
                validator.addError(rule);
                var fun = onUnpass || $.onUnpass;
                typeof (fun) == "function" ? fun(form[rule.name], rule) : null;
            } else if (mode == 4 && f === false) {
                //$.addError(rule);
                validator.addError(rule);
                var fun = onUnpass || $.onUnpass;
                if (typeof (fun) == "function") {
                    fun(form[rule.name], rule);
                    return false;
                }
            } else if (mode == 4 && f === true) {
                var fun = onPass || $.onPass;
                typeof (fun) == "function" ? fun(form[rule.name], rule) : null;
            }
        }

        if (mode == 1 && validator.errorList.length != 0) {
            //alert($.errToString());
            alert(validator.errorToString());
            return false;
        } else if (mode == 1) {
            return true;
        } else if (mode == 3 && validator.errorList.length != 0) {
            return false;
        } else if (mode == 3) {
            return true;
        }
    }

    var getValue = function( obj ){
        if(obj.length == undefined)
            return obj.value;
        else{
        
        }
    }

    var check = function (form, rule) {
        var type = (rule.type || "").toUpperCase();

        if (typeof rule.name == "string")
            obj = form[rule.name];
        else
            return null;
        
        // Note :not return false! ff 下，如果删除了一个表单项，form[itemname]还是存在的，但是 item.form 为 null,虽然 item.parentNode 也为 null,但是它却不能做判断条件，不明白！
        if (obj == undefined || ((obj.length == undefined || obj.length == null) && obj.form == null)) 
            return null; 

        if (obj.type == "checkbox" && obj.value.trim() == "") {
            obj.value = "on"; //FF,IE 下，如果 没有 value 的话，默认是 on
        }

        rule.required = (rule.required in {'true':1,'false':1}) ? rule.required : false;
        //如果 rule.type 是 Fun AND OR 必须向下检查
        //如果不是必填项，radio,checkbox,option可不向下检查。
        //如果不是必填项，除radio,checkbox,option外，如果值为空的话，可不向下检查。
        if (type != "FUN" && type != "AND" && type != "OR"
                && !(rule.required == true || type == "REQUIRE") 
                && (obj.length != undefined || obj.value == "")) {

            setErrorFlag(obj, rule, true);
            return true;
        } else if (rule.required == true && obj.length == undefined && !eval(REQUIRE.test(obj.value.trim()))) {
            setErrorFlag(obj, rule, false);
            return false;
        }

        //
        if (rule.type == undefined && rule.required !== true) return;

        var flag = true;
        switch (type) {
            //EntLib
            case "CONTAINS"://
            case "LENGTH"://
            case "NOTNULL"://
            case "RANGE":
            case "DATERANGE":
            case "RELATIVEDATETIME":
            //case "DOMAIN":
            case "COMPARE"://
            case "AND":
            case "OR":
            case "REGEX"://
            //
            case "DATE":
            case "TIME":
            case "REPEAT"://
            case "COMPARE":
            case "CUSTOM":
            case "GROUP":
            case "LIMIT":
            case "LIMITB":
            case "FILTER":
            case "INT":
            case "DOUBLE":
            case "NUM":
            case "FUN":
                if (!eval(eval(type))) {
                    flag = false;
                } else 
                    flag = true;
                break;
            default:
                if (type && !eval(type).test(obj.value)) {
                    flag = false;
                } else {
                    if (type == "REQUIRE" || rule.required == true) {
                        var min = (typeof rule.min != "number") ? 1 : rule.min;
                        var max = (typeof rule.max != "number") ? "" : rule.max;
                        var reg = new RegExp("^.{" + min + "," + max + "}$");

                        //Textarea 换行
                        //IE,opera /r
                        //FF,Safari /n
                        if (reg.test(obj.value.replace(/[\r|\n]/g, "").trim())) {
                            flag = true;
                        } else {
                            flag = false;
                        }
                    } else
                        flag = true;
                }
                break;
        }

        setErrorFlag(obj, rule, flag);
        return flag;
    }

    var setErrorFlag = function( obj, rule , flag){
        var att = "validationFlags";
        //checkbox , radiobox 等
        if(obj.length != undefined && obj.length > 0){
            obj = obj[obj.length - 1];
        }
        var vfs = (obj.getAttribute(att) || "").trim().split(/\s*,\s*/).join(',');
        if(flag === false){
            if((',' + vfs + ',').indexOf(',' + rule.id + ',') <0){
                vfs += ',' + rule.id;
                obj.setAttribute(att, vfs);
            }
        }else{
            vfs = (',' + vfs + ',').replace(',' + rule.id + ',', '');
            obj.setAttribute(att, vfs);
        }
    }

    $.allPass = function(obj){
        // checkbox radiobutton
        if(obj.length != undefined && obj.length > 0)
            obj = obj[0];

        return ((obj.getAttribute("ValidationFlags") || "").replace(/,/g, '') == "");
    }

    $.isDate = function (obj) {
        var r = obj.value.match(/^(\d{1,4})(-|\/)(\d{1,2})\2(\d{1,2})$/);
        if (r == null) return false;
        var d = new window.Date(r[1], Number(r[3]) - 1, r[4]);
        return (d.getFullYear() == r[1] && (d.getMonth() + 1) == r[3] && d.getDate() == r[4]);
    }

    $.isTime = function (obj) {
        var r = obj.value.match(/^(\d{2}):(\d{2}):(\d{2})$/)
        if (r == null) return false;
        var h = r[1], i = r[2], s = r[3];
        return h < 24 && i < 60 && s < 60;
    }

    $.mustChecked = function (obj, rule) {
        var o;
        var min_ = rule.min || 1, max_ = rule.max || obj.length || 1;

        var cn = 0;

        if (obj.length) {
            //多个 radio, CheckBox
            for (var i = 0; o = obj[i]; i++) {
                if (o.checked != undefined) {//Radio,CheckBox
                    var noSelected = rule.noSelected || "";
                    if(o.checked && noSelected == o.value) return false;
                    else if (o.checked)
                        cn++;
                } else {
                    // option
                    var noSelected = rule.noSelected || "";
                    if (o.selected && noSelected == o.value) return false;
                    else if (o.selected) cn++;
                }
            }
        } else {
            // 单个 Radio, CheckBox
            cn = obj.checked ? 1 : 0;
        }

        if (cn >= min_ && cn <= max_) return true;
        else return false;
    }

    //TODO 未测试
    $.checkNumber = function (type, obj, rule) {
        var f;
        switch (type) {
            case "Int":
                f = /^[-\+]?\d+$/.test(obj.value);
                break;
            case "Double":
                f = /^[-\+]?\d+(\.\d+)?$/.test(obj.value);
                break;
            //case "Number":
            default:
                f = !isNaN(Number(obj.value));
                break;
        }

        if (!f) return false;

        var min = (typeof rule.min != "number") ? Number.NEGATIVE_INFINITY : rule.min;
        var max = (typeof rule.max != "number") ? Number.POSITIVE_INFINITY : rule.max;
        return negated( rangeTest(obj.value, min, max, rule.includeMin, rule.includeMax), rule);


        //var min = rule.min | Number.NEGATIVE_INFINITY, max = rule.max | Number.POSITIVE_INFINITY;
        // 0 | null = 0   'a' | -2 = -2   -2 | 'a' = -2    'a' | 'a' = 0
        //用这个来判断数字。还可以。

//        var optMax = (rule.includeMax || true) ? "<=" : "<";
//        var optMin = (rule.includeMin || true) ? ">=" : ">";

        //return eval(obj.value + optMin + min + " && " + obj.value + optMax + max);

//        if (obj.value >= min && obj.value <= max)
//            return true;
//        else
//            return false;
    }

    $.doFilter = function (obj, rule) {
        var flag = new RegExp("^.+\.(?=EXT)(EXT)$".replace(/EXT/g, rule.accept.split(/\s*,\s*/).join("|")), "gi").test(obj.value);
        return negated(flag, rule);
    }

    $.testRegex = function (obj, rule) {
        if (rule.regex instanceof RegExp) {
            return negated( rule.regex.test(obj.value), rule);
        } else {
            alert("Validate rule not define regex.");
            return true;
        }
    }

    $.testFun = function (obj, rule) {
        var flag = true;
        if (rule["Fun"] instanceof Function) {
            flag = rule["Fun"](obj, rule);
            return negated(flag, rule);
        } else {
            alert("Validate rule not define fun.")
            return true;
        }
    }

    $.repeat = function (form, obj, rule) {
        var flag = form[rule.to].value == obj.value;
        return negated(flag, rule);
    }

    //TODO 应参照 rangeTest
    $.compare = function(form,obj, rule){
        if(!(rule.operator in {'>':1,'>=':1,'==':1,'<=':1, '<':1, '!=':1})){
            alert("错误的运算符, 必须是下面几种运算符之一:>,>=,=,<,<=,!=");
            return true;
        }
        var to = form[rule.to];
        if(to && to.value != undefined){
            // 比较 ： 数字，日期，字符串
            var flag = true;
            if(obj.value.isDate() && to.value.isDate()){
                flag = eval("'" + obj.value + "'.toDate()" + rule.operator + "'" + to.value + "'.toDate()");
            }else if(!isNaN(obj.value) && !isNaN(to.value)){
                flag = eval(obj.value + rule.operator + form[rule.to].value );
            }else{
                // new String('a') != new String('a')
                flag = eval("'" + obj.value.replace(/'/g,'\\\'') + "'" + rule.operator + "'" + form[rule.to].value.replace(/'/g,'\\\'') + "'" );
            }
            return negated(flag, rule);
        }else{
            alert("不存在比较对象");
            return true;
        }
    }


    $.strLen = function(obj, rule){
        var trim = rule.trim || false; //默认为trim
        var len = (trim ? obj.value.trim() : obj.value).length;
        var min = (typeof rule.min != "number") ? Number.NEGATIVE_INFINITY : rule.min;
        var max = (typeof rule.max != "number") ? Number.POSITIVE_INFINITY : rule.max;
        
        return negated(rangeTest(len, min,max, rule.includeMin, rule.includeMax), rule);
    }

    //TODO 如果没有设置 str 是返回 true 还是 false ?
    $.contains = function(obj, rule){
        if(typeof(rule.str) != "string" || rule.str.length == ''){
            alert("Contains 里没有设置 str");
            return true;
        }

        var mode;
        if(typeof(rule.mode) != "string" || !(rule.mode.toUpperCase() in {ALL:1, ANY:1})){
            mode = "ALL";
        }else{
            mode = rule.mode.toUpperCase();
        }

        var flag = true;
        if(mode == "ALL"){
            flag = obj.value.indexOf(rule.str) >= 0;
        }else{
            // ^ $ . * + ? = ! : | \ / ( ) [ ] { } 这些符号需在正则里有特殊含意
            var reg = new RegExp('[' + rule.str.replace(/[\^\$\.\*\+\?\=\!\:\|\\\/\(\)\[\]\{\}]/g, function( ma ){
                return '\\' + ma;
            }) + ']');
            //TODO 是否需要区分大小写?
            flag = reg.test(obj.value);
        }

        return negated(flag, rule);
    }

    //TODO 如果输入的不是日期时间格式,即使Negated也要返回 false
    $.dateRange = function(obj, rule){
        if(!obj.value.isDate())
            return false;

        return negated( rangeTest( obj.value.toDate(), rule.min.toDate, rule.max.toDate(), rule.includeMin, rule.includeMax ) , rule);
    }

    $.relativeDateRange = function(obj, rule){
        if(!obj.value.isDate())
            return false;

        var value = obj.value.toDate();
        if(typeof(rule.serverTime) == "string" && rule.serverTime.isDate() )
            targetTime = rule.serverTime.toDate();
        else
            targetTime = new Date();

        var min = max = null;
        if(rule.min != undefined){
            min = targetTime.add(rule.minUnit, rule.min);
        }

        if(rule.max != undefined){
            max = targetTime.add(rule.maxUnit, rule.max);
        }

        return negated( rangeTest(value , min, max, rule.includeMin, rule.includeMax), rule);
    }


    // And Composite 没有 msg
    $.and = function( form , rule ){
        if(typeof(rule.subRule) == "object" && rule.subRule.length > 0){
            var i, subRule;
            for(i=0;subRule = rule.subRule[i];i++){
                //子检查器里没有 name 和 required , 要从顶层的 and 检查器里拿
                subRule.name = rule.name;
                subRule.required = rule.required;
                subRule.label = rule.label;
                if(check(form, subRule) === false){//只要有一个子规则不成立，整个失败
                    rule.msg = subRule.msg;
                    return false;
                }
            }
            //所有子规则都通过
            return true;
        }
        // 无子规则，默认为成立
        return true;
    }

    // Or Composite 规则没有 Negated 属性
    $.or = function( form , rule ){
        if(typeof(rule.subRule) == "object" && rule.subRule.length > 0){
            var i, subRule;
            for(i=0;subRule = rule.subRule[i];i++){
                //子检查器没有 name 和 required, 要从 顶层的or 里取
                subRule.name = rule.name;
                subRule.required = rule.required;
                subRule.label = rule.label;
                if(check(form, subRule) !== false){//不严格等于 false 就是正确
                    return true;//其中一个子规则成立，整个 Or 成立
                }
            }
            return false;
        }
        // 无子规则，默认为成立
        return true;
    }

    //计算是否取反
    var negated = function( value, rule ){
        var neg = rule.negated || false;//false 不取反
        return neg ? !value : value;
    }

    //范围比较,数字
    var rangeTest = function(value, min, max, incMin, incMax ){
        var flag1 = true, flag2 = true;
        var optMax = (incMax || true) ? "<=" : "<";
        var optMin = (incMin || true) ? ">=" : ">";        
        
        if(min != undefined && min != null){
            //flag1 = eval(value + optMin + min);
            flag1 = eval("value" + optMin + "min");
        }

        if(max != undefined && max != null){
            //flag2 = eval(value + optMax + max);
            flag2 = eval("value" + optMax + "max");
        }
        return flag1 && flag2;
    }

    $.defaultMessageTemplate = {
        CONTAINS : "{label} : The value must contain the characters in \"{str}\" with mode \"{mode}\".",
        CONTAINS_N : "{label} : The value must not contain the characters in \"{str}\" with mode \"{mode}\".",

        LENGTH : "{label} : The length of the value must fall within the range \"{min}\" ({includeMin}) - \"{max}\" ({includeMax}).",
        LENGTH_N : "{label} : The length of the value must not fall within the range \"{min}\" ({includeMin}) - \"{max}\" ({includeMax}).",

        NOTNULL : "{label} : The value cannot be null.",
        NOTNULL_N : "{label} : The value must be null.",

        RANGE : "{label} : The value must fall within the range \"{min}\" ({includeMin}) - \"{max}\" ({includeMax}).",
        RANGE_N : "{label} : The value must not fall within the range \"{min}\" ({includeMin}) - \"{max}\" ({includeMax}).",

        RELATIVEDATETIME: "{label} : The value must fall within the range \"{min} {minUnit}\" ({includeMin}) - \"{max} {maxUnit}\" ({includeMax}) relative to now.",
        RELATIVEDATETIME_N : "{label} : The value must not fall within the range \"{min} {minUnit}\" ({includeMin}) - \"{max} {maxUnit}\" ({includeMax}) relative to now.",

        DOMAIN : "{label} : The value does not belong to the domain.",
        DOMAIN_N : "{label} : The value must not belong to the domain.",

        COMPARE : "{label} : {targetlabel}",
        COMPARE_N : "{label} : {targetlabel}",

        REGEX : "{label} : The value must match the regular expression \"{regex}\".",
        REGEX_N : "{label} : The value must not match the regular expression \"{regex}\".",

        UNIT_D : "Day(s)",
        UNIT_H : "Hour(s)",
        UNIT_MI : "Minute(s)",
        UNIT_M : "Month(s)",
        UNIT_S : "Second(s)",
        UNIT_YY: "Year(s)",
        
        INCLUDE : "Include",
        Exclude : "Exclude"
    };

    var customMessageTemplate = {};
    $.setMessageTemplate = function(template){
        customMessageTemplate = template;
    }

    var msgMatchReg = /{([^}]*)}/g;
    $.getMessage = function(rule){
        if(rule.msg)
            return rule.msg;
        else{
            var type = rule.type.toUpperCase();
            if(type == "AND" || type == "OR"){
                var msg = "";
                var i , sub;
                for(i = 0; sub = rule.subRule[i];i++){
                    sub.label = rule.label;
                    sub.targetLabel = rule.targetLabel;
                    msg += $.getMessage(sub) + " . ";
                }
                return msg;
            }else{
                if(rule.negated === true)
                    type += "_N";

                if(customMessageTemplate[type] != undefined || $.defaultMessageTemplate[type] != undefined){
                    var temp = customMessageTemplate[type] || $.defaultMessageTemplate[type];
                    var msg = temp.replace(msgMatchReg, function(){
                        var key = arguments[1];
                        if( rule[key] != undefined && rule[key] != null ){
                            var value;
                            if(key == "includeMin" || key == "includeMax"){
                                value =  rule[key] == true ? "INCLUDE" : "EXCLUDE";
                                value = customMessageTemplate[value] || $.defaultMessageTemplate[value];
                            }else if(key == "maxUnit" || key == "minUnit"){
                                type = "UNIT_" + rule[key].toUpperCase();
                                value = customMessageTemplate[type] || $.defaultMessageTemplate[type];
                            }else
                                value = rule[key];
                            return value;
                        }
                    });
                    return msg;
                }
			}
			return "";
		}
    }

})(FormValidateForEntLib);